﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using Aspose.Cells;
using ChanTai.WebApi.Data;
using ChanTai.WebApi.Exceptions;
using ChanTai.WebApi.Moudel;
using ChanTai.WebApi.Tpl;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using NVelocity.App;

namespace ChanTai.WebApi
{
    /// <summary>
    /// 诚泰接口
    /// </summary>
    [RoutePrefix("ChanTai")]
    public class ChanTaiApiController : ApiController
    {
        private static readonly log4net.ILog Log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);

        private static readonly string TplPath;
        private static readonly string SettingPath;
        private static readonly Dictionary<string, BusinsessItem> BusinsessMap; 
        static ChanTaiApiController()
        {
            
            TplPath = System.Configuration.ConfigurationManager.AppSettings.Get("Api.Tpl");
            if (string.IsNullOrEmpty(TplPath)) TplPath = "tpl";
            if (!TplPath.Contains(":")) TplPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, TplPath);
            if (!Directory.Exists((TplPath))) Directory.CreateDirectory(TplPath);
            SettingPath = System.Configuration.ConfigurationManager.AppSettings.Get("Api.SettingPath");
            if (string.IsNullOrEmpty(SettingPath)) SettingPath = "setting";
            if (!SettingPath.Contains(":")) SettingPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, SettingPath);
            if (!Directory.Exists((SettingPath))) Directory.CreateDirectory(SettingPath);
            var busfn = Path.Combine(SettingPath, "BusinessSource.txt");
            if (!File.Exists(busfn))
            {
                BusinsessMap = null;
                return;
            }
            try
            {
                var job = JsonConvert.DeserializeObject<List<BusinsessItem>>(File.ReadAllText(busfn));
                BusinsessMap = job.ToDictionary(s => s.code);
            }
            catch (Exception err)
            {
                Log.Error(err);
            }
        }

        private dynamic GetAutor(string autor)
        {
            var autorfn = Path.Combine(SettingPath, "autor.txt");
            if(!File.Exists(autorfn)) throw new Exception("配置渠道信息文件丢失。");
            try
            {
                var job = JObject.Parse(File.ReadAllText(autorfn));
                if(job.Properties().Any(s=>s.Name!= autor)) throw new Exception($"未配置渠道[{autor}]信息。");
                var ret = (JObject) job[autor];
                ret["qid"] = Guid.NewGuid().ToString("N");
                return (JObject) job[autor];
            }
            catch (Exception err)
            {
                Log.Error(err);
                throw new Exception("渠道配置文件格式错误", err);
            }
        }

        private ApiEngine NewApi<T>(T q, string tpl, Func<T,object> argfn = null) where T:Request
        {
            var vc = new ApiEngine();
            vc.SetTemplateDirPath(TplPath);
            vc.Put("istest", q.test);
            vc.SetTemplateDirPath(string.IsNullOrEmpty(q.autor)
                ? TplPath
                : Path.Combine(TplPath, q.autor));
            vc.SetTemplateFileName(tpl);
            vc.Put("autor", GetAutor(string.IsNullOrEmpty(q.autor) ? "default" : q.autor));
            vc.Put("args", argfn?.Invoke(q));
            return vc;
        }

        /// <summary>
        /// 团意险保单提交接口
        /// </summary>
        /// <param name="req">请求参数</param>
        /// <returns></returns>
        [Route("GroupAcciSubmit")]
        public AccSaveResult GroupAcciSubmit(AccSaveRequest req)
        {
            try
            {
                if (req == null) throw new WarningException("参数不能为空");
                var vc = NewApi(req, "submit.txt",s=>s.args);
                dynamic ret = vc.Post();
                return new AccSaveResult() {succeed = true, message = "", orderid = $"{ret?.Result?.Body?.ftrno}" ,bdh = $"{ret?.Result?.Body?.policyNo}"};
            }
            catch (WarningException err)
            {
                if(err.InnerException!=null) Log.Error(err);
                return new AccSaveResult() { message = err.Message };
            }
            catch (Exception err)
            {
                Log.Error(err);
#if DEBUG
                return new AccSaveResult() { message = err.ToString() };
#else
                return new AccSaveResult() {message = err.Message};
#endif
            }

        }

        /// <summary>
        /// 团意险导入excel计算保费
        /// </summary>
        /// <param name="req"></param>
        /// <returns></returns>
        [Route("GroupAcciCalculate")]
        public GroupAcciCalculateResult GroupAcciCalculate(GroupAcciCalculateRequest req)
        {
            try
            {
                if(req==null) throw new WarningException("参数不能为空");
                if (string.IsNullOrEmpty(req.excl)) throw new WarningException("数据文件参数不能为空");
                if (!File.Exists(req.excl)) throw new WarningException("找不到数据文件参数或被删除");
                var cfgpath = string.IsNullOrEmpty(req.autor) ? SettingPath : Path.Combine(SettingPath, req.autor);
                var prod = GetProduct(cfgpath, "groupacci");
                var rest = CalculatGroupFromExcel(req.excl, cfgpath);
                var dtc =
                rest.GroupBy(s => $"{s.zylb}-{s.acciBe}-{s.medicalBe}-{s.allowanceBe}", s => s)
                    .ToDictionary(s => s.Key, s => s.Select(g => g).ToList());
                Log.Info($"groupCount:{dtc.Count}");
                List<Plan> plans = new List<Plan>();
                var smbf = 0d;
                var smbe = 0d;
                var smjf = 0d;
                foreach (var pitem in dtc)
                {
                    var tplan = new Plan { itemAcci = new ItemAcci { bationType = false } };
                    var p0 = pitem.Value[0];
                    tplan.itemAcci.insuredBusinessSource = p0.zylb;
                    tplan.itemAcci.quantity = pitem.Value.Count;
                    tplan.itemKindList = new List<ItemKind>();
                    tplan.itemKindList.Add(new ItemKind
                    {
                        kindCode = prod.kinds["acci"].code,
                        kindInd = prod.kinds["acci"].type,
                        rate = p0.acciRoat,
                        sumInsured = p0.acciBe,
                        grossPremium = p0.acciBf
                    });
                    if (p0.medicalBe > 0)
                        tplan.itemKindList.Add(new ItemKind
                        {
                            kindCode = prod.kinds["medical"].code,
                            kindInd = prod.kinds["medical"].type,
                            rate = p0.medicalRoat,
                            sumInsured = p0.medicalBe,
                            grossPremium = p0.medicalBf
                        });
                    if (p0.allowanceBe > 0)
                        tplan.itemKindList.Add(new ItemKind
                        {
                            kindCode = prod.kinds["allowance"].code,
                            kindInd = prod.kinds["allowance"].type,
                            rate = p0.allowanceRoat,
                            sumInsured = p0.allowanceBe,
                            grossPremium = p0.allowanceBf
                        });
                    tplan.riskRealateParyList = new List<RiskRealatePary>();
                    foreach (var item in pitem.Value)
                    {
                        var risk = new RiskRealatePary
                        {
                            clientCName = item.name,
                            insuredBusinessSource = item.zylb
                        };
                        risk.setInsuredIdNo(item.idno);
                        tplan.riskRealateParyList.Add(risk);
                    }
                    plans.Add(tplan);
                    smbe += p0.totalBe * tplan.itemAcci.quantity;
                    smbf += p0.totalBf * tplan.itemAcci.quantity;
                    smjf += p0.totalJf * tplan.itemAcci.quantity;
                    //Console.WriteLine($"{pitem.Key}\n{JsonConvert.SerializeObject(pitem.Value)}\n\n");
                }
                req.uwCount = req.uwCount < 1 ? 1 : req.uwCount;
                smbe = Math.Round(smbe* req.uwCount, 2);
                smbf = Math.Round(smbf , 2);
                var bf = Math.Round(smbf * req.uwCount, 2);
                smjf = Math.Round(smjf * req.uwCount, 2);
                var grp = new GroupSubmit()
                {
                    Main = new AcciMain()
                    {
                        sumInsured = smbe,
                        sumGrossPremium = bf,
                        commissionRatio = Math.Round(1 - smjf / smbf, 2),
                        planCode = prod.planCode,
                        startDate =$"{req.startDate.ToString("yyyy-MM-dd")} 00:00:00",
                        endDate = $"{req.endDate.ToString("yyyy-MM-dd")} 23:59:59",
                        operateDate = DateTime.Now.ToString("yyyy-MM-dd")
                    },
                    RelatedParty = new RelatedParty()
                    {
                        insuredName = req.tbr,
                        identifyType = req.idType,
                        identifyNumber = req.tbrIdno,
                        insuredType = req.tbrType,
                        uWCount = req.uwCount
                    },
                    PlanList = plans
                };
                return new GroupAcciCalculateResult() {succeed = true, data = grp, insureItems = rest,uwCount = req.uwCount,totalBf = bf,price=smbf};
            }
            catch (WarningException err)
            {
                return new GroupAcciCalculateResult() { message = err.Message };
            }
            catch (Exception err)
            {
                Log.Error(err);
#if DEBUG
                return new GroupAcciCalculateResult() { message = err.ToString() };
#else
                return new GroupAcciCalculateResult() {message = err.Message};
#endif
            }
        }

        /// <summary>
        /// 获取产品配置信息
        /// </summary>
        /// <param name="configpath"></param>
        /// <param name="key"></param>
        /// <returns></returns>
        private Product GetProduct(string configpath, string key)
        {
            var prodfn = Path.Combine(configpath, @"product.txt");
            if (!File.Exists(prodfn)) throw new WarningException("产品配置文件丢失");
            Dictionary<string, Product> prodmap;
            try
            {
                prodmap = JsonConvert.DeserializeObject<Dictionary<string, Product>>(File.ReadAllText(prodfn));
            }
            catch (Exception e)
            {
                throw new WarningException("产品配置文件格式错误", e);
            }
            if (!prodmap.ContainsKey("groupacci")) throw new WarningException("未配置团意外险产品");
            return prodmap["groupacci"];
        }

        /// <summary>
        /// excel计算团险保费
        /// </summary>
        /// <param name="fn"></param>
        /// <param name="configpath"></param>
        /// <returns></returns>
        private List<GroupInsureItem> CalculatGroupFromExcel(string fn, string configpath)
        {
            if(BusinsessMap==null) throw new WarningException("职业编码配置文件丢失或格式不正确");
            if (!File.Exists(fn)) throw new WarningException("导入数据文件丢失");
            var mpfn = Path.Combine(configpath, @"map.txt");
            var roatfn = Path.Combine(configpath, @"roat.txt");
            
            if (!File.Exists(mpfn)) throw new WarningException("导入数据规则文件丢失");
            if (!File.Exists(roatfn)) throw new WarningException("费率配置文件丢失");
            Dictionary<string, ExeclMapItem> map;
            try
            {
                map = JsonConvert.DeserializeObject<Dictionary<string, ExeclMapItem>>(File.ReadAllText(mpfn));
            }
            catch (Exception e)
            {
                throw new WarningException("导入数据规则文件格式错误", e);
            }
            Dictionary<string, Dictionary<string, Dictionary<string, RoatItem>>> roatmap;
            try
            {
                roatmap = JsonConvert.DeserializeObject<Dictionary<string, Dictionary<string, Dictionary<string, RoatItem>>>>(File.ReadAllText(roatfn));
            }
            catch (Exception e)
            {
                throw new WarningException("费率配置文件格式错误", e);
            }
            
            if (!map.ContainsKey("groupacci")) throw new WarningException("未配置团意外险excel解析规则");
            var gmap = map["groupacci"];
            if (!roatmap.ContainsKey("groupacci")) throw new WarningException("未配置团意外险费率规则");
            var grout = roatmap["groupacci"];

            

            if (!grout.ContainsKey("acci")) throw new WarningException("未批准主险acci费率");
            if (!grout.ContainsKey("medical")) throw new WarningException("附件医疗险medical费率");
            if (!grout.ContainsKey("allowance")) throw new WarningException("住院津贴allowance费率");
            var wk = new Workbook(fn);
            var cells = wk.Worksheets[gmap.SheetPos].Cells;
            var jar = new JArray();
            for (int i = 1; i < cells.MaxRow; i++)
            {
                var job = new JObject();
                for (int j = 0; j < gmap.Fields.Count; j++)
                {
                    job[gmap.Fields[j]] = cells[i, j].StringValue;
                }
                jar.Add(job);
            }
            var jstr = jar.ToString();
            //Console.WriteLine(jstr);
            var dt = JsonConvert.DeserializeObject<List<GroupInsureItem>>(jstr);
            var errlist = new List<string>();
            foreach (GroupInsureItem item in dt)
            {
                var ermsg = new List<string>();
                if(!BusinsessMap.ContainsKey(item.zylb)) ermsg.Add($"编号‘{item.id}’职业编码‘{item.zylb}’存在");
                else
                {
                    var zykind = BusinsessMap[item.zylb].kind;
                    if (!grout["acci"].ContainsKey(zykind))
                    {
                        ermsg.Add($"主险不保该职业‘{zykind}’未配置费率");
                        //throw new Exception($"编号‘{item.id}’主险不保该职业‘{item.zylb}’或未配置费率");
                    }
                    else item.setAcciRoat(grout["acci"][zykind]);
                    if (!grout["medical"].ContainsKey(zykind))
                    {
                        ermsg.Add($"住院医疗险该职业‘{zykind}’未配置费率");
                        //throw new Exception($"编号‘{item.id}’主险不保该职业‘{item.zylb}’或未配置费率");
                    }
                    else item.setMedicalRoat(grout["medical"][zykind]);
                    if (!grout["allowance"].ContainsKey(zykind))
                    {
                        ermsg.Add($"住院津贴险该职业‘{zykind}’未配置费率");
                        //throw new Exception($"编号‘{item.id}’主险不保该职业‘{item.zylb}’或未配置费率");
                    }
                    else item.setAallowanceRoatRoat(grout["allowance"][zykind]);
                    if (ermsg.Count == 0) item.calculat();
                    else errlist.Add($"编号‘{item.id}’{string.Join(",", ermsg)}");
                }
            }
            if (errlist.Count == 0) return dt;
            throw new WarningException($"{string.Join("\n", errlist)}");
        }
    }
}
